﻿using System;
using System.Linq;
using System.Net.Sockets;

namespace server05_udp
{
    class UdpHost
    {
        //UDP 通讯非常直白，无需专门的服务器建筑
        Socket socket;
        System.Collections.Concurrent.ConcurrentStack<SocketAsyncEventArgs> sendArgs = new System.Collections.Concurrent.ConcurrentStack<SocketAsyncEventArgs>();
        public delegate void deleRecv(byte[] data, System.Net.IPEndPoint remote);
        public event deleRecv OnRecv;
        public void Start(int port)
        {
            System.Net.IPEndPoint ep = new System.Net.IPEndPoint(System.Net.IPAddress.Any, port);
            this.socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            this.socket.Bind(ep);

            ////开多少接收的
            for (var i = 0; i < 100; i++)
            {
                var recvArgs = new SocketAsyncEventArgs();
                recvArgs.Completed += OnCompleted;
                recvArgs.RemoteEndPoint = new System.Net.IPEndPoint(0L, 0);
                recvArgs.SetBuffer(new byte[1500], 0, 1500);
                this.socket.ReceiveFromAsync(recvArgs);
            }
        }
        void OnCompleted(object sender, SocketAsyncEventArgs e)
        {
            if (e.LastOperation == SocketAsyncOperation.ReceiveFrom)
            {
                if (e.SocketError == SocketError.Success)
                {
                    int len = e.BytesTransferred;
                    try
                    {
                        this.OnRecv?.Invoke(e.Buffer.Take(len).ToArray(), e.RemoteEndPoint as System.Net.IPEndPoint);
                    }
                    catch (Exception err)
                    {

                    }

                }
                this.socket.ReceiveAsync(e);
            }
            if(e.LastOperation== SocketAsyncOperation.SendTo)
            {
                //現在SocketAsyncEventArgs 發送時 那邊 new 這邊銷毀 比較浪費
                e.Dispose();
            }
        }
        public void Send(byte[] data, System.Net.IPEndPoint to)
        {
            //這玩意兒要循環復用
            var recvArgs = new SocketAsyncEventArgs();
            recvArgs.Completed += OnCompleted;
            recvArgs.RemoteEndPoint = to;
            recvArgs.SetBuffer(data, 0, data.Length);
            this.socket.SendToAsync(recvArgs);
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            int port = 8001;

            var host = new UdpHost();

            host.OnRecv += (data, remote) =>
             {
                 Console.WriteLine("recv:" + data.Length + " from:" + remote);
                 host.Send(new byte[] { 3, 2, 1 },remote);
             };

            host.Start(port);
            Console.WriteLine("start udp server at:" + port);
            while (true)
            {
                Console.ReadLine();
            }
        }
    }
}
